package com.wjerp.tplus.server.domain.service.impl;

import com.alibaba.fastjson.JSON;
import com.wjerp.tplus.server.api.request.AddInvoiceInfoRequest;
import com.wjerp.tplus.server.api.request.QueryInvoiceListRequest;
import com.wjerp.tplus.server.api.response.FieldValueResponse;
import com.wjerp.tplus.server.api.response.InvoiceInfoResponse;
import com.wjerp.tplus.server.common.constant.AccountChangeType;
import com.wjerp.tplus.server.common.constant.DeleteFlag;
import com.wjerp.tplus.server.common.constant.InvoiceCheckStatus;
import com.wjerp.tplus.server.common.constant.RedisKey;
import com.wjerp.tplus.server.common.exception.BusinessException;
import com.wjerp.tplus.server.common.exception.ServiceException;
import com.wjerp.tplus.server.common.model.InvoiceCheckResponse;
import com.wjerp.tplus.server.common.utils.WjOpenApiUtils;
import com.wjerp.tplus.server.common.utils.RedisUtils;
import com.wjerp.tplus.server.common.utils.SpringContextUtil;
import com.wjerp.tplus.server.domain.entity.*;
import com.wjerp.tplus.server.domain.entity.invoice.mng.InvoiceInfo;
import com.wjerp.tplus.server.domain.entity.invoice.mng.ReimburseMemberInfo;
import com.wjerp.tplus.server.domain.entity.sys.UserInfo;
import com.wjerp.tplus.server.domain.mapper.*;
import com.wjerp.tplus.server.domain.service.InvoiceInfoService;
import com.wjerp.tplus.server.domain.service.SystemInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * @author lishuailei
 */
@Service
public class InvoiceInfoServiceImpl implements InvoiceInfoService {

    private static final Logger logger = LoggerFactory.getLogger("InvoiceInfoServiceImpl");

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private WjOpenApiUtils wjOpenApiUtils;

    @Resource
    private ReimburseMemberInfoMapper reimburseMemberInfoMapper;

    @Resource
    private InvoiceInfoMapper invoiceInfoMapper;

    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private CompanyInvoiceRelationMapper companyInvoiceRelationMapper;

    @Resource
    private CompanyAccountInfoMapper companyAccountInfoMapper;

    @Resource
    private CompanyInfoMapper companyInfoMapper;

    @Resource
    private CompanyAccountChangeInfoMapper companyAccountChangeInfoMapper;

    @Override
    public InvoiceInfoResponse addInvoiceInfo(AddInvoiceInfoRequest request, String displayUserId) throws BusinessException, ServiceException {
        return addInvoiceInfo(request,displayUserId,true);
    }

    @Override
    public InvoiceInfoResponse addInvoiceInfo(AddInvoiceInfoRequest request, String displayUserId, boolean isAsync) throws BusinessException, ServiceException {
        //校验输入参数是否合法
        checkRequestParam(request);

        //发票唯一号码
        String invoiceUuidCode = request.getCompanyCode() + request.getInvoiceNumber();

        redisUtils.tryLock(RedisKey.ADD_INVOICE + invoiceUuidCode,invoiceUuidCode,30);
        try {
            //1.校验发票号码是否存在
            Object invoiceObj = redisUtils.getMap(RedisKey.INVOICE_POOL + request.getCompanyCode() , invoiceUuidCode);
            InvoiceInfo invoiceInfo = null;
            if (invoiceObj != null) {
                invoiceInfo = JSON.parseObject(invoiceObj.toString(), InvoiceInfo.class);
                if(invoiceInfo.getIsDeleted().equals(DeleteFlag.UN_DELETE)) {
                    InvoiceInfoResponse invoiceInfoResponse = new InvoiceInfoResponse();
                    BeanUtils.copyProperties(invoiceInfo, invoiceInfoResponse);
                    ReimburseMemberInfo reimburseMemberInfo = reimburseMemberInfoMapper.selectByMemberCodeAndCompanyCode(invoiceInfo.getReimburseMemberCode(), request.getCompanyCode());
                    invoiceInfoResponse.setReimburseMemberName(reimburseMemberInfo.getName());
                    UserInfo userInfo = userInfoMapper.selectByDisplayUserId(invoiceInfo.getCreatedUser());
                    invoiceInfoResponse.setCreatedUsername(userInfo.getName());
                    return invoiceInfoResponse;
                }
            }
            //2.保存发票记录到数据库
            Boolean isNewInvoice = invoiceInfo == null;
            if(isNewInvoice) {
                invoiceInfo = new InvoiceInfo();
            }
            BeanUtils.copyProperties(request, invoiceInfo);
            invoiceInfo.setCode(invoiceUuidCode);
            invoiceInfo.setIsDeleted(0);
            invoiceInfo.setCreatedUser(displayUserId);
            invoiceInfo.setUpdatedUser(displayUserId);

            int result = 0;
            if(isNewInvoice){
                result = invoiceInfoMapper.insertSelective(invoiceInfo);
            }else{
                result = invoiceInfoMapper.updateByPrimaryKeySelective(invoiceInfo);
            }

            if (result <= 0) {
                throw new BusinessException("保存发票信息失败，请重试");
            }
            //3.增加Redis 记录
            redisUtils.setMap(RedisKey.INVOICE_POOL + request.getCompanyCode() , invoiceUuidCode, JSON.toJSONString(invoiceInfo));

            //4.增加发票和公司之间的关系
            if(isNewInvoice) {
                createCompanyAndInvoiceRelation(invoiceInfo, request.getCompanyCode(), displayUserId);
            }
            //4.发票信息查验
            if(isAsync){
                RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
                InvoiceInfo finalInvoiceInfo = invoiceInfo;
                CompletableFuture.runAsync(()->{
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    try {
                        verInvoice(finalInvoiceInfo,request.getCompanyCode());
                    } catch (ServiceException e) {
                        e.printStackTrace();
                        logger.error("verInvoice error,{}",e);
                    }
                });
            }else{
                verInvoice(invoiceInfo,request.getCompanyCode());
            }
        }finally {
            //3.释放锁
            redisUtils.unlock(RedisKey.ADD_INVOICE + invoiceUuidCode,invoiceUuidCode);

        }
        return null;
    }

    private void createCompanyAndInvoiceRelation(InvoiceInfo invoiceInfo, String companyCode, String displayUserId) {
        CompanyInvoiceRelation companyInvoiceRelation = new CompanyInvoiceRelation();
        companyInvoiceRelation.setCompanyCode(companyCode);
        companyInvoiceRelation.setInvoiceUuidCode(invoiceInfo.getCode());
        companyInvoiceRelation.setCreatedUser(displayUserId);
        companyInvoiceRelation.setUpdatedUser(displayUserId);
        companyInvoiceRelation.setCreatedTime(new Date());
        companyInvoiceRelation.setUpdatedTime(new Date());
        companyInvoiceRelationMapper.insertSelective(companyInvoiceRelation);
    }


    @Override
    public List<InvoiceInfo> loadInvoiceInfoList(QueryInvoiceListRequest request) {
        return invoiceInfoMapper.selectInvoiceInfoListByRequest(request);
    }

    @Override
    public Integer loadInvoiceInfoListCount(QueryInvoiceListRequest request) {
        return invoiceInfoMapper.selectInvoiceInfoListCountByRequest(request);
    }



    @Override
    public InvoiceCheckResponse loadInvoiceContentInfoByInvoiceUuidCode(String invoiceUuidCode) {
        InvoiceInfo invoiceInfo = invoiceInfoMapper.selectByCode(invoiceUuidCode);
        if(invoiceInfo == null){
            return  null;
        }

        String checkResponse = invoiceInfo.getCheckResponse();
        if(StringUtils.isEmpty(checkResponse)){
            return  null;
        }
        return JSON.parseObject(checkResponse,InvoiceCheckResponse.class);
    }

    @Override
    public InvoiceInfo loadInvoiceInfoByInvoiceUuidCode(String invoiceUuidCode) {
        return invoiceInfoMapper.selectByCode(invoiceUuidCode);
    }

    @Override
    public void deleteInvoiceByCode(String invoiceUuidCode, String displayUserId) throws ServiceException {
        InvoiceInfo invoiceInfo = invoiceInfoMapper.selectByCode(invoiceUuidCode);
        if(invoiceInfo == null){
            throw new ServiceException("发票信息未找找到");
        }
        invoiceInfo.setIsDeleted(DeleteFlag.DELETE);
        invoiceInfo.setUpdatedUser(displayUserId);
        invoiceInfoMapper.updateByPrimaryKeySelective(invoiceInfo);

        //删除发票和公司之间的关系
        CompanyInvoiceRelation companyInvoiceRelation = companyInvoiceRelationMapper.selectByInvoiceUuidCode(invoiceInfo.getCode());

        //修改Redis中记录
        redisUtils.setMap(RedisKey.INVOICE_POOL + companyInvoiceRelation.getCompanyCode() , invoiceUuidCode,JSON.toJSONString(invoiceInfo));
    }

    @Override
    public void reVerInvoice(String invoiceUuidCode,String companyCode, String displayUserId) throws ServiceException {
        InvoiceInfo invoiceInfo = loadInvoiceInfoByInvoiceUuidCode(invoiceUuidCode);
        verInvoice(invoiceInfo,companyCode);
    }


    /**
     * 校验添加发票入参是否合法
     * @param request
     */
    private void checkRequestParam(AddInvoiceInfoRequest request) throws BusinessException {
        if(StringUtils.isEmpty(request.getInvoiceCode())){
            throw new BusinessException("发票代码不能为空");
        }

        if(StringUtils.isEmpty(request.getInvoiceNumber())){
            throw new BusinessException("发票号码不能为空");
        }

        if(StringUtils.isEmpty(request.getCheckCode())){
            throw new BusinessException("发票校验码不能为空");
        }

        if(StringUtils.isEmpty(request.getInvoiceDate())){
            throw new BusinessException("开票日期不能为空");
        }

        if(StringUtils.isEmpty(request.getAmount())){
            throw new BusinessException("发票金额不能为空");
        }

        if(StringUtils.isEmpty(request.getReimburseMemberCode())){
            throw new BusinessException("报销人员不能为空");
        }
    }

    /**
     * 执行发票查验
     * @param invoiceInfo 发票信息
     * @param companyCode 企业编码
     */
    private void verInvoice(InvoiceInfo invoiceInfo, String companyCode) throws ServiceException {
        try {
            logger.info("verInvoice:开始执行发票信息查验,{}", invoiceInfo);
            CompanyAccountInfo companyAccountInfo = null;
            try {
                FieldValueResponse systemInfo = null;

                //获取企业配置信息是否需要进行查验
                SystemInfoService systemInfoService = SpringContextUtil.getBeanByClass(SystemInfoService.class);
                systemInfo = systemInfoService.loadSysConfigInfoByCompanyCode(companyCode);
                if(!systemInfo.getCheckInvoice()){
                    //表示不查验
                    logger.info("verInvoice:系统设置不进行查验,{}", invoiceInfo);
                    throw new ServiceException("系统设置不进行查验");
                }

                //1.获取企业的账户信息
                companyAccountInfo = companyAccountInfoMapper.selectByCompanyCode(companyCode);
                if(companyAccountInfo == null){
                    logger.error("企业账户信息获取失败，企业编码：{}",companyCode);
                    throw new ServiceException("企业账户信息获取失败");
                }

                if(companyAccountInfo.getCheckNum().equals(0)){
                    //增加标记
                    redisUtils.setMap(RedisKey.COMPANY_ACCOUNT_MAP,RedisKey.ACCOUNT_BALANCE_ZERO+companyCode,companyCode);
                    throw new ServiceException("查验失败原因账户余额为零");
                }


                InvoiceInfo invoiceInfoResponse = wjOpenApiUtils.verInvoice(invoiceInfo,companyCode);

                if(invoiceInfoResponse.getCheckStatus().equals(InvoiceCheckStatus.SUCCESS.getCode()) ){
                    InvoiceCheckResponse invoiceCheckResponse = JSON.parseObject(invoiceInfoResponse.getCheckResponse(), InvoiceCheckResponse.class);
                    if( systemInfo.getCheckCompanyName()){
                        //表示进行公司名称查验
                        CompanyInfo companyInfo = companyInfoMapper.selectByCompanyCode(companyCode);

                        if(companyInfo.getName().equals(invoiceCheckResponse.getBuyerCompanyName())){
                            invoiceInfo.setCheckStatus(InvoiceCheckStatus.SUCCESS.getCode());
                            invoiceInfo.setCheckMessage(invoiceCheckResponse.getMessage());
                        }else{
                            invoiceInfo.setCheckStatus(InvoiceCheckStatus.FAIL_BY_NAME.getCode());
                            invoiceInfo.setCheckMessage(InvoiceCheckStatus.FAIL_BY_NAME.getMessage());
                        }
                    }else{
                        invoiceInfo.setCheckStatus(InvoiceCheckStatus.SUCCESS.getCode());
                        invoiceInfo.setCheckMessage(invoiceCheckResponse.getMessage());
                    }

                    invoiceInfo.setCheckResponse(JSON.toJSONString(invoiceCheckResponse));
                    invoiceInfo.setSaleCompanyName(invoiceCheckResponse.getSaleCompanyName());

                }else{
                    invoiceInfo.setCheckStatus(invoiceInfoResponse.getCheckStatus());
                    invoiceInfo.setCheckMessage(invoiceInfoResponse.getCheckMessage());
                }
            } catch (ServiceException e) {
                invoiceInfo.setCheckStatus(InvoiceCheckStatus.FAIL.getCode());
                invoiceInfo.setCheckMessage(e.getErrorMsg());
            }
            invoiceInfo.setUpdatedTime(new Date());
            invoiceInfoMapper.updateByPrimaryKeySelective(invoiceInfo);
            //扣减企业的账户余额
            changeCompanyAccount(invoiceInfo.getCode(),invoiceInfo.getCheckStatus(), companyAccountInfo, AccountChangeType.SUBTRACT_CHECK_NUM);
        }catch (Exception ex){
            ex.printStackTrace();
            logger.error("verInvoice:执行发票信息查验失败,{}", ex);
            throw new ServiceException("执行发票信息查验失败,"+ex.getMessage());
        }
    }

    /**
     * 账户变更操作
     * @param checkStatus 校验状态
     * @param companyAccountInfo 账户信息
     * @param accountChangeType 变更类型
     */
    private void changeCompanyAccount(String relationItemCode, Integer checkStatus, CompanyAccountInfo companyAccountInfo, AccountChangeType accountChangeType) {
        if(checkStatus.equals(InvoiceCheckStatus.FAIL.getCode())){
            //校验失败，不做账户变更操作
            return;
        }

        //2.执行账户变更操作
        int count = companyAccountInfoMapper.updateCompanyAccount( companyAccountInfo.getId(),accountChangeType.getCode(),1,"0");
        if(count > 0){
            //增加变更历史
            CompanyAccountChangeInfo companyAccountChangeInfo = new CompanyAccountChangeInfo();
            companyAccountChangeInfo.setRelationItemCode(relationItemCode);
            companyAccountChangeInfo.setAccountCode(companyAccountInfo.getAccountCode());
            companyAccountChangeInfo.setUpdatedUser("0");
            companyAccountChangeInfo.setCreatedUser("0");
            companyAccountChangeInfo.setActionType(accountChangeType.getCode());
            companyAccountChangeInfo.setPreCheckNum(companyAccountInfo.getCheckNum());
            companyAccountChangeInfo.setPreOcrNum(companyAccountInfo.getOcrNum());

            companyAccountInfo = companyAccountInfoMapper.selectByPrimaryKey(companyAccountInfo.getId());

            companyAccountChangeInfo.setPostCheckNum(companyAccountInfo.getCheckNum());
            companyAccountChangeInfo.setPostOcrNum(companyAccountInfo.getOcrNum());
            companyAccountChangeInfo.setRemark(accountChangeType.getMessage());

            companyAccountChangeInfoMapper.insertSelective(companyAccountChangeInfo);
        }
    }
}
